
/* Generated by Interface Builder */

#import "defs.h"
#import "ContourView.h"
#import <appkit/SavePanel.h>
#import <appkit/color.h>
#import <appkit/PrintPanel.h>
#import <appkit/Button.h>
#import <appkit/Pasteboard.h>
extern int contour_(int *jdim, int *kdim, int *nj, int *nk, 
		    float *x, float *y, float *f, int *ncont, float *acont,
		    float *ppxunit, float *ppyunit, int *colorMap);

@implementation ContourView

- (BOOL) acceptsFirstMouse { return YES;}    /* grab that mouse down event! */

- clear:sender
{
  NXEraseRect(&bounds);
  NXSetColor([contourParam provideBackGroundColor]);
  NXRectFill(&bounds);
  return self;
}


- setDrawColor:(float) color
{
  PSsetgray(color);
  return self;
}

- drawSelf: (const NXRect *)rects :(int)rectCount{

  BOOL functionDraw = [contourParam doDrawFunction];
  BOOL gridDraw     = [contourParam doDrawGrid];
  BOOL clearPlot    = [contourParam doClearPlot];
  float  xmin = [contourParam provideXmin];
  float  xmax = [contourParam provideXmax];
  float  ymin = [contourParam provideYmin];
  float  ymax = [contourParam provideYmax];
  const char * maintitle = [contourParam provideMainTitle];
  const char * functiontitle = [contourParam provideFunctionTitle];
  const char * xtitle = "x";
  const char * ytitle = "y";
  char unitLabel[40];
  id titleFont, labelFont;
  float   xwid, yhgt=14.0;
  float xsave;
  float pattern0[] = {};	/* solid      */
  int   viewChoice =   [contourParam provideViewingChoice:self];
  int   viewPoint  =   [contourParam provideViewPoint:self];
  float h,s,br;
  int inc_cont,icont,count_cont;
  int number_of_contours;	/* the number of contour levels */
  float *contour_levels;	/* the contour level values */
  NXColor color;
  int lineColor;

  lineColor =  [contourParam provideContourLineColor];

  if(clearPlot)[self clear:self];

  NXSetColor([contourParam provideForeGroundColor]);

  if ([contourParam shouldChangeTitleFont]) {
    newTitleFont =
      [theFontManager convertFont:[theFontManager selFont]];
  }

  if (newTitleFont) {
    titleFont = [Font newFont:[newTitleFont name] 
		   size:[newTitleFont pointSize]
		   style:[newTitleFont style]
		   matrix:NX_IDENTITYMATRIX];
    yhgt = [newTitleFont pointSize];
  }
  else {
    titleFont =
      [Font newFont:"Helvetica" size:14.0 style:0 matrix:NX_IDENTITYMATRIX];
    yhgt = 14.0;
  }
  [titleFont set];

  xwid = [titleFont getWidthOf:maintitle];

  PSmoveto(XOFFSET/4.0 + (bounds.size.width - xwid)/2.0, 
	   bounds.size.height - 2.0 - yhgt);
  PSshow((char *)maintitle);

  if([contourParam shouldShowXYLabels]){

    xwid = [titleFont getWidthOf:functiontitle];

    PSmoveto(bounds.size.width-xwid-18.,
		   bounds.size.height-40.0);
    PSshow((char *)functiontitle);


    switch(viewChoice){

    case 0:
      xtitle = "x";
      ytitle = "y";
      break;
    case 1:
      xtitle = "x";
      ytitle = "z";
      break;
    case 2:
      xtitle = "y";
      ytitle = "z";
      break;
    case 3:
      xtitle = "i";
      ytitle = "j";
      break;
    case 4:
      xtitle = "y";
      ytitle = "x";
      break;
    case 5:
      xtitle = "z";
      ytitle = "x";
      break;
    case 6:
      xtitle = "z";
      ytitle = "y";
      break;
    }

    xwid = [titleFont getWidthOf:xtitle];

    PSmoveto(XOFFSET/4.0 + (bounds.size.width - xwid)/2.0, 
	     (bounds.origin.y + 10.0));
    PSshow((char *)xtitle);

    xwid = [titleFont getWidthOf:ytitle];
    yhgt = 10.0;
    PSmoveto((bounds.origin.x + 10.0 + yhgt), 
	     YOFFSET/4.0 + (bounds.size.height - xwid)/2.0);

    PSgsave();
    PSrotate(90.0);
    PSshow((char *)ytitle);
    PSgrestore();
  }


  PSgsave();

  PStranslate(XOFFSET, YOFFSET);

  ppxunit = 0.88*(bounds.size.width-XOFFSET)/ABS(xmax-xmin);
  ppyunit = 0.88*(bounds.size.height-YOFFSET)/ABS(ymax-ymin);
  

  if([contourParam doSetAspectRatio]){

    if(ABS(xmax-xmin) >= ABS(ymax-ymin)){
      if(ABS(ymax-ymin)*ppxunit >= 0.88*(bounds.size.height-YOFFSET))
	{ ppxunit = ppyunit;} else {ppyunit = ppxunit; } 
    }
    else{  
      if(ABS(xmax-xmin)*ppyunit >= 0.88*(bounds.size.width-XOFFSET))
	{ ppyunit = ppxunit;} else {ppxunit = ppyunit; }
    }
  }

  if(viewPoint == 1){
    if(xmax > xmin){
      xsave = xmax;
      xmax = xmin;
      xmin = xsave;
      [contourParam resetXmin:xmin];
      [contourParam resetXmax:xmax];
    }
    ppxunit = -ppxunit;
  }
  else{
    if(xmax < xmin){
      xsave = xmax;
      xmax = xmin;
      xmin = xsave;
      [contourParam resetXmin:xmin];
      [contourParam resetXmax:xmax];
    }
  }

// try this
//  if(ppxunit >= ppyunit){ppyunit = ppxunit;} else { ppxunit = ppyunit;}

  xmin = xmin*ppxunit;	/* drawing is all in pixel coordinates */
  xmax = xmax*ppxunit;
  ymin = ymin*ppyunit;
  ymax = ymax*ppyunit;


  PStranslate(-xmin, -ymin);
  
  if(clearPlot){
    PSsetlinewidth(0);

    NXSetColor([contourParam provideForeGroundColor]);
    
    PSnewpath();		/* draw bounding box */
    PSmoveto(xmin, ymin);
    PSlineto(xmax, ymin);
    PSlineto(xmax, ymax);
    PSlineto(xmin, ymax);
    PSclosepath();
    PSstroke();			/* finish bounding box */

    if ([contourParam shouldChangeLabelFont]) {
      newLabelFont =
	[theFontManager convertFont:[theFontManager selFont]];
    }
      if (newLabelFont) {
	labelFont = [Font newFont:[newLabelFont name] 
		   size:[newLabelFont pointSize]
		   style:[newLabelFont style]
		   matrix:NX_IDENTITYMATRIX];
	yhgt = [newLabelFont pointSize];
      }
      else {
	labelFont =
	  [Font newFont:"Courier" size:12.0 style:0 matrix:NX_IDENTITYMATRIX];
	yhgt = 12.0;
      }

    [labelFont set];

    /*  draw unit labels */
    if([contourParam shouldShowUnitsLabel]){

      PSmoveto(xmin, ymin - 4.0); /* xmin unit label */
      PSsetdash(pattern0, 0, 0.0);
      PSrlineto(0.0, 4.0);
      PSstroke();
      sprintf(unitLabel, "%10.4g", xmin/ppxunit);
      xwid = [labelFont getWidthOf:unitLabel];
      PSmoveto(xmin - xwid/2.0, ymin - yhgt - 5.0);
      PSshow(unitLabel);

      PSmoveto(xmax, ymin - 4.0); /* xmax unit label */
      PSsetdash(pattern0, 0, 0.0);
      PSrlineto(0.0, 4.0);
      PSstroke();
      sprintf(unitLabel, "%10.4g", xmax/ppxunit);
      xwid = [labelFont getWidthOf:unitLabel];
      PSmoveto(xmax - xwid/2.0, ymin - yhgt - 5.0);
      PSshow(unitLabel);

      PSmoveto(xmin - 4.0, ymin ); /* ymin unit label */
      PSsetdash(pattern0, 0, 0.0);
      PSrlineto(4.0, 0.0);
      PSstroke();
      sprintf(unitLabel, "%10.4g", ymin/ppyunit);
      xwid = [labelFont getWidthOf:unitLabel];
      PSmoveto(xmin - xwid - 10.0, ymin + 2.0 - yhgt/2.0);
      PSshow(unitLabel);

      PSmoveto(xmin - 4.0, ymax ); /* ymax unit label */
      PSsetdash(pattern0, 0, 0.0);
      PSrlineto(4.0, 0.0);
      PSstroke();
      sprintf(unitLabel, "%10.4g", ymax/ppyunit);
      xwid = [labelFont getWidthOf:unitLabel];
      PSmoveto(xmin - xwid - 10.0, ymax + 2.0 - yhgt/2.0);
      PSshow(unitLabel);

    }
  }
 
  /* MIN and MAX below in case xmin > xmax  and/or  ymin > ymax. */
  PSrectclip(MIN(xmin,xmax), MIN(ymin,ymax), ABS(xmax-xmin), ABS(ymax-ymin));
  
  if(gridDraw)[self gridAndDraw];
  if(functionDraw)[self contourAndDraw];
  
  PSgrestore();

  if(clearPlot){


    /*  draw contour labels */
    if([contourParam shouldShowContourValues]){

      if ([contourParam shouldChangeLabelFont]) {
	newLabelFont =
	  [theFontManager convertFont:[theFontManager selFont]];
      }

      if (newLabelFont) {
	labelFont = [Font newFont:[newLabelFont name] 
		   size:[newLabelFont pointSize]
		   style:[newLabelFont style]
		   matrix:NX_IDENTITYMATRIX];
	yhgt = [newLabelFont pointSize];
      }
      else {
	labelFont =
	  [Font newFont:"Courier" size:12.0 style:0 matrix:NX_IDENTITYMATRIX];
	yhgt = 12.0;
      }

      [labelFont set];

      number_of_contours = [contourParam provideContourNumber:self];
      if(number_of_contours != 0){
	contour_levels = [contourParam provideContourLevels:self];

	sprintf(unitLabel, "%10.6g", contour_levels[0]);
	xwid = [labelFont getWidthOf:unitLabel];

	inc_cont = 1;
	if(number_of_contours > 50)inc_cont =(int)(number_of_contours/20);

	count_cont = 0;
	for (icont=0; icont < number_of_contours; icont = icont + inc_cont)
	  {
	    count_cont = count_cont+1;
	    switch(lineColor){
	    case 0:  [self setDrawColor:NX_LTGRAY]; break;
	    case 1:  [self setDrawColor:NX_DKGRAY]; break;
	    case 2:  [self setDrawColor:NX_BLACK]; break;
	    case 3: 
	      h = 0.6666*icont/(float)number_of_contours;
	      s = 1.0;
	      br = 1.0;
	      color = NXConvertHSBToColor(h,s,br);
	      NXSetColor(color);
	      break;
	    }
	    sprintf(unitLabel, "%10.6g", contour_levels[icont]);
	    PSmoveto(bounds.size.width-xwid-24.,
		     bounds.size.height-count_cont*(yhgt+3.0)-40.0);
	    PSshow(unitLabel);
	  }
	
	if(number_of_contours - count_cont*inc_cont != 0)
	  {
	    switch(lineColor){
	    case 0:  [self setDrawColor:NX_LTGRAY]; break;
	    case 1:  [self setDrawColor:NX_DKGRAY]; break;
	    case 2:  [self setDrawColor:NX_BLACK]; break;
	    case 3: 
	      h = 0.6666*1.0;
	      s = 1.0;
	      br = 0.6;
	      color = NXConvertHSBToColor(h,s,br);
	      NXSetColor(color);
	      break;
	    }
	    sprintf(unitLabel, "%10.6g", contour_levels[number_of_contours]);
	    PSmoveto(bounds.size.width-xwid-8.,
		     bounds.size.height-(count_cont+1)*(yhgt+3.0)-40.0);
	    PSshow(unitLabel);
	  }
      }
    }
  }

  return self;
}

- contourAndDraw
{
  int colorMap;
  int lineColor =  [contourParam provideContourLineColor];
  function_part function;	/* the struct of our function */
  int number_of_contours;	/* the number of contour levels */
  float *contour_levels;	/* the contour level values */

  function = [contourParam provideFunction:self]; /* get function */

  number_of_contours = [contourParam provideContourNumber:self];

  contour_levels = [contourParam provideContourLevels:self];

  if([contourParam doSolidLines])colorMap = 0;
  if(![contourParam doSolidLines])colorMap = 1;

  PSsetlinewidth([contourParam provideContourLineThickness]);

  switch(lineColor){
  case 0:  [self setDrawColor:NX_LTGRAY]; break;
  case 1:  [self setDrawColor:NX_DKGRAY]; break;
  case 2:  [self setDrawColor:NX_BLACK]; break;
  case 3:  colorMap = 2; if(![contourParam doSolidLines])colorMap = 3; break;
  }

  contour_(&function.jmax, &function.kmax, &function.jmax,
	   &function.kmax, function.x, function.y, function.f, 
	   &number_of_contours, contour_levels, &ppxunit, &ppyunit, &colorMap);

  return self;
}

- gridAndDraw
{

  int i,j,k;

  grid_part grid;	/* the struct of our function */

  grid = [contourParam provideGrid:self]; /* get grid */
  
  PSsetlinewidth([contourParam provideGridLineThickness]);
  
  NXSetColor([contourParam provideGridLineColor]);


  /* j family */
  for (k=0; k < grid.kmax; k++){
    i = k*grid.jmax;
    PSnewpath();
    PSmoveto(grid.x[i]*ppxunit, grid.y[i]*ppyunit);
    for (j=1; j < grid.jmax; j++){
      i = k*grid.jmax + j;
      PSlineto(grid.x[i]*ppxunit, grid.y[i]*ppyunit);
    }
    PSstroke();
  }

  /* k family */
  for (j=0; j < grid.jmax; j++){
    PSnewpath();
    PSmoveto(grid.x[j]*ppxunit, grid.y[j]*ppyunit);
    for (k=1; k < grid.kmax; k++){
      i = k*grid.jmax + j;
      PSlineto(grid.x[i]*ppxunit, grid.y[i]*ppyunit);
    }
    PSstroke();
  }

  return self;
}

- doPrinting:sender
{

/* to customize print panel */
  id myPrintPanel = [PrintPanel new];

  [ [NXApp printInfo] setMarginLeft:72.0 right:72.0 top:72.0 bottom:72.0 ];

  if (bounds.size.height > bounds.size.width) { /* portrait mode */
    if (bounds.size.height/bounds.size.width > 9.0/6.5) {
      [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.height];
    }
    else {
      [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.width];
    }
    [ [NXApp printInfo] setOrientation:NX_PORTRAIT andAdjust:YES ];
  }
  else {			/* landscape mode */
    if (bounds.size.width/bounds.size.height > 9.0/6.5) {
      [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.width];
    }
    else {
      [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.height];
    }
    [ [NXApp printInfo] setOrientation:NX_LANDSCAPE andAdjust:YES ];
  }

  [myPrintPanel setAccessoryView:printColorAccessory];

  [self printPSCode:sender];
  return self;
}

- mouseDown:(NXEvent *)e{
/*
 * This code taken from the Mandelbrot example in /NextDeveloper (and modified).
 * We implement the mouseDown method so the user can sweep out a section of
 * the view and select that as the current window in which to view the curve(s).
 */

  int looping = YES;
  int oldMask;
  NXRect bbox;
  NXPoint startPoint, currPoint;
  float xmin, xmax, ymin, ymax;
  BOOL zooming = [contourParam doZoom];

  if (zooming) {

    xmin = [contourParam provideXmin] * ppxunit;
    xmax = [contourParam provideXmax] * ppxunit;
    ymin = [contourParam provideYmin] * ppyunit;
    ymax = [contourParam provideYmax] * ppyunit;
  
  
    oldMask =  [window addToEventMask:NX_MOUSEDRAGGEDMASK];
    startPoint = e->location;
    [self convertPoint:&startPoint fromView:nil];
    NXSetRect(&bbox,startPoint.x,startPoint.y,0.0,0.0);
    [self lockFocus];
    while (looping) {
      e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
      currPoint = e->location;
      [self convertPoint:&currPoint fromView:nil];
      bbox.size.width = (currPoint.x - startPoint.x);
      bbox.size.height = (currPoint.y - startPoint.y);
      /* Normalize bbox to always have positive width and height */
      if (bbox.size.width < 0) {
	bbox.size.width = -bbox.size.width;
	bbox.origin.x   = startPoint.x - bbox.size.width;
      }
      if (bbox.size.height < 0) {
	bbox.size.height = -bbox.size.height;
	bbox.origin.y    = startPoint.y - bbox.size.height;
      }
      /*
       * constrain the box to have the aspect ratio of the view.  Choose
       * whichever dimension is closer to the desired result.
       */
      PSnewinstance();
      if (looping = (e->type == NX_MOUSEDRAGGED)) {
	PSsetinstance(YES);
	NXSetColor([contourParam provideForeGroundColor]);
//	PSsetgray(NX_BLACK);
	NXFrameRect(&bbox);
	PSsetinstance(NO);
      }
    }

    [self unlockFocus];
    [window setEventMask:oldMask];

    if ((bbox.size.width > 0) && (bbox.size.height > 0)) {
      /* At this point, bbox is in window coordinates.  Convert to curve
       * coordinates based on this view's coordinates and the bounding box.
       */
      
      xmin = xmin + (bbox.origin.x - XOFFSET);
      xmax = xmin + bbox.size.width;
      ymin = ymin + (bbox.origin.y - YOFFSET);
      ymax = ymin + bbox.size.height;

      /* save old min/max */
      [contourParam stackOldMinMax:xmin/ppxunit :xmax/ppxunit
                                 :ymin/ppyunit :ymax/ppyunit];

      [contourParam resetXmin:xmin/ppxunit];
      [contourParam resetXmax:xmax/ppxunit];
      [contourParam resetYmin:ymin/ppyunit];
      [contourParam resetYmax:ymax/ppyunit];

      /*  Call [self display] to force current values of xmin/xmax/ymin/ymax
       *  to take effect (otherwise xmin, etc., get incremented too many times).
       */
      [contourParam drawPlotButton:1];
      [self display];
      [contourParam drawPlotButton:0];
    }
  }
  return self;
}



- initFrame:(const NXRect *)frameRect
{
  [super initFrame:frameRect];
  /* a place to init stuff  thp 6/12/91 */
  return self;
}

- saveEPS:sender
{
    id savePanel = [SavePanel new];
    char  *eps_fileName;	// Name of the EPS file for output

    [savePanel setTitle:"SAVE EPS FILE"];
    [savePanel setRequiredFileType:"eps"];
    if ([savePanel runModal])  {
      eps_fileName = (char *)calloc(strlen([savePanel filename])+1, 
				    sizeof(char));
      strcpy(eps_fileName, [savePanel filename]);
      [self savePSCode:eps_fileName];
    }

    return self;
}

- savePSCode:(char *)aFile
{
    NXStream	*psStream;

    psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
    if (!psStream)  {
      return self;
    }
    [self getBounds:&bounds];
    [self copyPSCodeInside:&bounds to:psStream];
	   
    NXFlush(psStream);
    NXSaveToFile(psStream, aFile);
    NXCloseMemory(psStream, NX_FREEBUFFER);

    return self;
}

/*
 * Following code taken from the Graph example in NextDeveloper/Examples.
 *
 * Copies the current view into the Pasteboard as PostScript.
 */
- copyPScode:sender
{
    NXStream *psStream;
    id        pb;
    char     *data;
    int       dataLen, maxDataLen;

  /* Open a stream on memory where we will collect the PostScript */
    psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
    if (!psStream)
      return self;

  /* Tell the Pasteboard we're going to copy PostScript */
    pb = [Pasteboard new];
    [pb declareTypes:&NXPostScriptPboardType num:1 owner:self];

  /* writes the PostScript for the whole plot as EPS into the stream */
    [self getBounds:&bounds];
    [self copyPSCodeInside:&bounds to:psStream];

  /* get the buffered up PostScript out of the stream */
    NXGetMemoryBuffer(psStream, &data, &dataLen, &maxDataLen);

  /* put the buffer in the Pasteboard, free the stream (and the buffer) */
    [pb writeType:NXPostScriptPboardType data:data length:dataLen];
    NXCloseMemory(psStream, NX_FREEBUFFER);
    return self;
}

@end
